package com.yyb.service.impl;

import com.yyb.constants.CoreConstant;
import com.yyb.domain.SysMenu;
import com.yyb.domain.SysUser;
import com.yyb.dto.SysMenuAddDTO;
import com.yyb.dto.SysMenuUpdateDTO;
import com.yyb.enums.StateEnums;
import com.yyb.exception.BusinessException;
import com.yyb.mapper.SysMenuMapper;
import com.yyb.mapper.SysRoleMapper;
import com.yyb.service.SysMenuService;
import com.yyb.vo.RouterVO;
import com.yyb.vo.SysMenuVO;
import jakarta.annotation.Resource;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 
 * 
 * 
 */
@Service
@Log4j2
public class SysMenuServiceImpl extends BaseServiceImpl<SysMenu> implements SysMenuService {

    @Resource
    private SysMenuMapper sysMenuMapper;

    @Resource
    private SysRoleMapper sysRoleMapper;

    /**
     * 查询所有菜单权限数据
     *
     * @return
     */
    @Override
    public List<SysMenuVO> queryAllMenu() {
        SysUser currentUser = getCurrentUser();
        //查询出来的数据
        List<SysMenu> sysMenus = null;
        if (currentUser.getUserType().equals(StateEnums.USER_TYPE_ADMIN.getCode())) {
            //说明是超级管理员，查询所有目录，菜单，权限
            sysMenus = this.sysMenuMapper.queryAllMenu();
        } else {
            //说明是普通用户，根据用户ID查询目录菜单权限
            sysMenus = this.sysMenuMapper.queryAllMenuByUserId(currentUser.getUserId());
        }
        //把 List<SysMenu>转成List<SysMenuVO>
        List<SysMenuVO> sysMenuVOS = sysMenus.stream()
                .map(sysMenu -> {
                    SysMenuVO sysMenuVO = new SysMenuVO();
                    BeanUtils.copyProperties(sysMenu, sysMenuVO);
                    return sysMenuVO;
                }).collect(Collectors.toList());
        return convertMenuTree(sysMenuVOS, CoreConstant.DEFAULT_PARENT_ID);
    }

    /**
     * 查询所有菜单和目录
     *
     * @return
     */
    @Override
    public List<SysMenuVO> queryAllMenuMC() {
        SysUser currentUser = getCurrentUser();
        //查询出来的数据
        List<SysMenu> sysMenus = null;
        if (currentUser.getUserType().equals(StateEnums.USER_TYPE_ADMIN.getCode())) {
            //说明是超级管理员，查询所有目录，菜单
            sysMenus = this.sysMenuMapper.queryAllMCMenu();
        } else {
            //说明是普通用户，根据用户ID查询目录菜单
            sysMenus = this.sysMenuMapper.queryAllMCMenuByUserId(currentUser.getUserId());
        }
        //把 List<SysMenu>转成List<SysMenuVO>
        List<SysMenuVO> sysMenuVOS = sysMenus.stream()
                .map(sysMenu -> {
                    SysMenuVO sysMenuVO = new SysMenuVO();
                    BeanUtils.copyProperties(sysMenu, sysMenuVO);
                    return sysMenuVO;
                }).collect(Collectors.toList());
        return convertMenuTree(sysMenuVOS, CoreConstant.DEFAULT_PARENT_ID);
    }

    /**
     * 递归
     * 把没有层级关系的 List<SysMenuVO> 转成有层级关系的
     *
     * @param sysMenuVOS
     * @param parentId
     * @return
     */
    private List<SysMenuVO> convertMenuTree(List<SysMenuVO> sysMenuVOS, Long parentId) {
        List<SysMenuVO> roots = sysMenuVOS.stream()
                .filter(sysMenuVO -> sysMenuVO.getParentId().equals(parentId))
                .collect(Collectors.toList());
        roots.forEach(r -> r.setChildren(convertMenuTree(sysMenuVOS, r.getMenuId())));
        return roots;
    }

    /**
     * 根据菜单ID查询菜单
     *
     * @param menuId
     * @return
     */
    @Override
    public SysMenu selectByPrimaryKey(Long menuId) {
        return this.sysMenuMapper.selectByPrimaryKey(menuId);
    }

    /**
     * 添加菜单权限
     *
     * @param sysMenuAddDTO
     * @return
     */
    @Override
    public int insert(SysMenuAddDTO sysMenuAddDTO) {
        //创建要保存的对象
        SysMenu sysMenu = new SysMenu();
        //copy
        BeanUtils.copyProperties(sysMenuAddDTO, sysMenu);
        //填充其它数据
        sysMenu.setCreateBy(getUsername());
        sysMenu.setCreateTime(new Date());
        sysMenu.setUpdateTime(new Date());
        //处理menuId
        Integer menuType = sysMenu.getMenuType();
        Long parentId = sysMenu.getParentId();
        int row = 0;
        //自旋锁解决并发问题
        while (true) {
            //根据菜单类型和菜单的父节点的ID查询菜单ID的最大值
            Long maxMenuId = this.sysMenuMapper.queryMenuMaxIdByMenuTypeAndParentId(menuType, parentId);
            if (ObjectUtils.isEmpty(maxMenuId)) {
                if (menuType.equals(StateEnums.MENU_M.getCode())) {
                    //说明是目录type=0
                    sysMenu.setMenuId(1L);
                } else {
                    sysMenu.setMenuId(Long.parseLong(parentId + "01"));
                }
            } else {
                //说明不为空 在原有的id基础上+1
                sysMenu.setMenuId(maxMenuId + 1);
            }
            try {
                row = this.sysMenuMapper.insertSelective(sysMenu);
                break;
            } catch (Exception e) {
                log.error("主键重复");
            }
        }
        return row;
    }

    /**
     * 修改菜单权限
     *
     * @param sysMenuUpdateDTO
     * @return
     */
    @Override
    public int updateByPrimaryKey(SysMenuUpdateDTO sysMenuUpdateDTO) {
        //创建要保存的对象
        SysMenu sysMenu = new SysMenu();
        //copy
        BeanUtils.copyProperties(sysMenuUpdateDTO, sysMenu);
        //填充其它数据
        sysMenu.setUpdateBy(getUsername());
        sysMenu.setUpdateTime(new Date());
        return this.sysMenuMapper.updateByPrimaryKeySelective(sysMenu);
    }

    /**
     * 删除菜单权限
     *
     * @param menuId
     * @return
     */
    @Override
    public int deleteByPrimaryKey(Long menuId) {
        //根据menuId查询parentId=menuId的数量 如果数量大于0 就说明当前menuId下面有子节点
        Long count = this.sysMenuMapper.queryChildrenCountByParentId(menuId);
        if (count > 0) {
            throw new BusinessException("当前菜单权限下有子节点，请先删除子节点");
        }
        sysRoleMapper.deleteRoleMenuByMenuId(menuId);
        return this.sysMenuMapper.deleteByPrimaryKey(menuId);
    }

    /**
     * 获取动态路由
     * 如果是超级管理员，可以看到所有目录和菜单
     * 如果是普通用户，只能看到自己拥有的菜单和目录
     *
     * @return
     */
    @Override
    public List<RouterVO> getRouters() {
        Integer userType = getCurrentUser().getUserType();
        Long userId = getCurrentUser().getUserId();
        List<SysMenu> menuList = null;
        if (userType.equals(StateEnums.USER_TYPE_ADMIN.getCode())) {
            menuList = this.sysMenuMapper.queryAllMCMenu();
        } else {
            menuList = this.sysMenuMapper.queryAllMCMenuByUserId(userId);
        }
        //把List<SysMenu> 转成List<SysMenuVO>
        List<SysMenuVO> sysMenuVOS = menuList.stream()
                .map(sysMenu -> {
                    SysMenuVO sysMenuVO = new SysMenuVO();
                    BeanUtils.copyProperties(sysMenu, sysMenuVO);
                    return sysMenuVO;
                }).collect(Collectors.toList());
        //建立层有关系
        List<SysMenuVO> treeMenuList = convertMenuTree(sysMenuVOS, CoreConstant.DEFAULT_PARENT_ID);
        //把treeMenuList 转成List<RouterVO>
        return buildRouters(treeMenuList);
    }

    /**
     * 把有层级关系的List<SysMenuVO> treeMenuList转成有层有关系的List<RouterVO>
     *
     * @param treeMenuList
     * @return
     */
    private List<RouterVO> buildRouters(List<SysMenuVO> treeMenuList) {
        List<RouterVO> routerVOList = new ArrayList<>();
        //把routerVOList里面的所有数据转成List<RouterVO>
        treeMenuList.forEach(sysMenuVO -> {
            //创建RouterVO
            RouterVO routerVO = new RouterVO();
            //菜单名称
            routerVO.setName(sysMenuVO.getMenuName());
            //处理path 因为数据库里面的path前面没有/ 而如果是目录 路由时必须要 /  否则不用
            Integer menuType = sysMenuVO.getMenuType();
            routerVO.setPath(menuType.equals(StateEnums.MENU_M.getCode()) ? CoreConstant.URL_SPLIT + sysMenuVO.getPath() : sysMenuVO.getPath());
            //处理component 如果菜单类型是目录 那么就是Layout  否则就是sysMenuVO里面的component
            routerVO.setComponent(menuType.equals(StateEnums.MENU_M.getCode()) ? CoreConstant.DEFAULT_COMPONENT : sysMenuVO.getComponent());
            //处理是否显示
            routerVO.setHidden(sysMenuVO.getVisible().equals(CoreConstant.HIDDEN_STATE));
            //处理是否是直显示
            routerVO.setAlwaysShow(menuType.equals(StateEnums.MENU_M.getCode()));
            //设置是否在面包屑导航中点击
            routerVO.setRedirect(menuType.equals(StateEnums.MENU_M.getCode()) ? CoreConstant.NO_REDIRECT : null);
            //组装metaVo
            RouterVO.MetaVO metaVO = new RouterVO.MetaVO(sysMenuVO.getMenuName(), sysMenuVO.getIcon());
            //把metaVO 给routerVO
            routerVO.setMeta(metaVO);
            if (!sysMenuVO.getChildren().isEmpty()) {
                //递归处理子节点
                routerVO.setChildren(buildRouters(sysMenuVO.getChildren()));
            }
            routerVOList.add(routerVO);
        });
        return routerVOList;
    }

}
